#pragma once

#include <cstdint>
#include <string>
#include <vector>

namespace hude
{
    namespace safe
    {
        /*********************************************************************
         * @brief BASE64编解码实现细节
         */
        namespace details_b64
        {
            /*********************************************************************
             * @brief BASE64编解码实现细节
             */
            static const unsigned char b64_chr[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

            /*********************************************************************
             * @brief BASE64编解码实现细节
             */
            inline unsigned int b64_int(unsigned int ch)
            {

                // ASCII to base64_int
                // 65-90  Upper Case  >>  0-25
                // 97-122 Lower Case  >>  26-51
                // 48-57  Numbers     >>  52-61
                // 43     Plus (+)    >>  62
                // 47     Slash (/)   >>  63
                // 61     Equal (=)   >>  64~
                if (ch == 43)
                    return 62;
                if (ch == 47)
                    return 63;
                if (ch == 61)
                    return 64;
                if ((ch > 47) && (ch < 58))
                    return ch + 4;
                if ((ch > 64) && (ch < 91))
                    return ch - 'A';
                if ((ch > 96) && (ch < 123))
                    return (ch - 'a') + 26;
                return 0;
            }

            /*********************************************************************
             * @brief BASE64编解码实现细节
             */
            inline unsigned int b64e_size(unsigned int in_size)
            {

                // size equals 4*floor((1/3)*(in_size+2));
                int i, j = 0;
                for (i = 0; (size_t)i < in_size; i++)
                {
                    if (i % 3 == 0)
                        j += 1;
                }
                return (4 * j);
            }

            /*********************************************************************
             * @brief BASE64编解码实现细节
             */
            inline unsigned int b64d_size(unsigned int in_size)
            {

                return ((3 * in_size) / 4);
            }

            /*********************************************************************
             * @brief BASE64编解码实现细节
             */
            inline unsigned int b64_encode(const unsigned char *in, unsigned int in_len, unsigned char *out)
            {

                unsigned int i = 0, j = 0, k = 0, s[3];

                for (i = 0; i < in_len; i++)
                {
                    s[j++] = *(in + i);
                    if (j == 3)
                    {
                        out[k + 0] = b64_chr[(s[0] & 255) >> 2];
                        out[k + 1] = b64_chr[((s[0] & 0x03) << 4) + ((s[1] & 0xF0) >> 4)];
                        out[k + 2] = b64_chr[((s[1] & 0x0F) << 2) + ((s[2] & 0xC0) >> 6)];
                        out[k + 3] = b64_chr[s[2] & 0x3F];
                        j = 0;
                        k += 4;
                    }
                }

                if (j)
                {
                    if (j == 1)
                        s[1] = 0;
                    out[k + 0] = b64_chr[(s[0] & 255) >> 2];
                    out[k + 1] = b64_chr[((s[0] & 0x03) << 4) + ((s[1] & 0xF0) >> 4)];
                    if (j == 2)
                        out[k + 2] = b64_chr[((s[1] & 0x0F) << 2)];
                    else
                        out[k + 2] = '=';
                    out[k + 3] = '=';
                    k += 4;
                }

                out[k] = '\0';

                return k;
            }

            /*********************************************************************
             * @brief BASE64编解码实现细节
             */
            inline unsigned int b64_decode(const unsigned char *in, unsigned int in_len, unsigned char *out)
            {

                unsigned int i = 0, j = 0, k = 0, s[4];

                for (i = 0; i < in_len; i++)
                {
                    s[j++] = b64_int(*(in + i));
                    if (j == 4)
                    {
                        out[k + 0] = ((s[0] & 255) << 2) + ((s[1] & 0x30) >> 4);
                        if (s[2] != 64)
                        {
                            out[k + 1] = ((s[1] & 0x0F) << 4) + ((s[2] & 0x3C) >> 2);
                            if ((s[3] != 64))
                            {
                                out[k + 2] = ((s[2] & 0x03) << 6) + (s[3]);
                                k += 3;
                            }
                            else
                            {
                                k += 2;
                            }
                        }
                        else
                        {
                            k += 1;
                        }
                        j = 0;
                    }
                }

                return k;
            }

        } //namespace details_b64

        /*********************************************************************
         * @brief BASE64编解码
         */
        struct base64_t
        {
            /*********************************************************************
             * @brief 计算输出长度
             */
            struct size
            {
                /*********************************************************************
                 * @brief 计算编码
                 * @param siz: 输入长度
                 * @return 正常返回输出长度，0为错误
                 */
                static std::size_t encode( std::size_t siz )
                {
                    return details_b64::b64e_size( siz );
                }
                /*********************************************************************
                 * @brief 计算解码
                 * @param siz: 输入长度
                 * @return 正常返回输出长度，0为错误
                 */
                static std::size_t decode( std::size_t siz )
                {
                    return details_b64::b64d_size( siz );
                }
            };

            /*********************************************************************
             * @brief 编码
             * @param dat: 输入数据
             * @param cnt: 输入长度
             * @param out: 输出数据
             * @return 正常返回输出长度，0为错误
             */
            static std::size_t encode( const uint8_t* dat, std::size_t cnt, char* out )
            {
                return details_b64::b64_encode( (const uint8_t*)dat, cnt, (uint8_t*)out );
            }

            /*********************************************************************
             * @brief 解码
             * @param dat: 输入数据
             * @param cnt: 输入长度
             * @param out: 输出数据
             * @return 正常返回输出长度，0为错误
             */
            static std::size_t decode( const char* dat, std::size_t cnt, uint8_t* out )
            {
                return details_b64::b64_decode( (const uint8_t*)dat, cnt, (uint8_t*)out );
            }

            /*********************************************************************
             * @brief 编码
             * @param dat: 输入数据
             * @param out: 输出数据
             * @param cnt: 输入长度, 0为根据输入数据的长度
             * @return 正常返回输出长度，0为错误
             */
            static std::size_t encode( const std::vector< uint8_t >& dat, std::string& out, std::size_t cnt = 0 )
            {
                if( cnt == 0 ) cnt = dat.size();

                out.resize( details_b64::b64e_size( cnt ), '\0' );

                auto e = details_b64::b64_encode( (const uint8_t*)&(dat[0]), cnt, (uint8_t*)&(out[0]) );

                out[e] = '\0';

                return (std::size_t)e;
            }

            /*********************************************************************
             * @brief 解码
             * @param dat: 输入数据
             * @param out: 输出数据
             * @param cnt: 输入长度, 0为根据输入数据的长度
             * @return 正常返回输出长度，0为错误
             */
            static std::size_t decode( const std::string& dat, std::vector< uint8_t >& out, std::size_t cnt = 0 )
            {
                if( cnt == 0 ) cnt = dat.size();

                out.resize( details_b64::b64d_size( cnt ), '\0' );

                auto e = details_b64::b64_decode( (const uint8_t*)&(dat[0]), cnt, (uint8_t*)&(out[0]) );

                out.resize( e );

                return (std::size_t)e;
            }

            /*********************************************************************
             * @brief 编码
             * @param dat: 输入数据
             * @param cnt: 输入长度, 0为根据输入数据的长度
             * @return 输出结果
             */
            static std::string encode( const std::vector< uint8_t >& dat, std::size_t cnt = 0 )
            {
                std::string ret;

                return encode( dat, ret, 0U ), ret;
            }

            /*********************************************************************
             * @brief 解码
             * @param dat: 输入数据
             * @param cnt: 输入长度, 0为根据输入数据的长度
             * @return 输出结果
             */
            static std::vector< uint8_t > decode( const std::string& dat, std::size_t cnt = 0 )
            {
                std::vector< uint8_t > ret;

                return decode( dat, ret, 0U ), ret;
            }
        };

    } //namespace safe
} //namespace hude